home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / snz128s / src / testmall.c < prev    next >
C/C++ Source or Header  |  1994-04-13  |  25KB  |  684 lines

  1. /*******************************************************************************
  2. * Module       : TESTMALL.C                                                    *
  3. *------------------------------------------------------------------------------*
  4. * Program      : DEBUG UTILITY                                                 *
  5. *------------------------------------------------------------------------------*
  6. * Author       : M.R.Watson.   (maff@cix)                                      *
  7. *------------------------------------------------------------------------------*
  8. * Version      : 1.0                                                           *
  9. *------------------------------------------------------------------------------*
  10. * Legal Status : Public Domain.                                                *
  11. *------------------------------------------------------------------------------*
  12. * Last Updated : 05/07/92                                                      *
  13. *==============================================================================*
  14. * Contains a set of routines for checking if any malloced blocks have had data *
  15. * overwritten of the end or the beginning of them. Also checks for illegal     *
  16. * frees and can display lists of unfreed mallocs.                              *
  17. ********************************************************************************
  18.  
  19. Uses
  20. ----
  21.  
  22. These functions allow you to do the following:
  23.  
  24. * Check if any illegal memory writes have been made within a given distance
  25.   of a malloced or calloced buffer. The module name and line number where
  26.   any offending buffers were allocated is displayed, along with the
  27.   whereabouts of call to free.
  28.  
  29. * Optionally print out all calls to malloc, calloc and free, along with
  30.   the module name and line numbers where they were called.
  31.  
  32. * Check what outstanding mallocs there are at any time. This is very useful
  33.   for pinpointing memory leaks.
  34.  
  35. * Checks if any blocks are being freed twice or, equivalently, if a memory
  36.   block not allocated with malloc() or calloc() is being freed.
  37.  
  38. How it works
  39. ------------
  40.  
  41. When you #include "testmall.h", a set of macros replace calls to malloc,
  42. calloc and free with calls to special debug versions. These versions should
  43. not be called directly, but only with the macros, which automatically add
  44. module name and line number parameters to the calls.
  45.  
  46. The replacement malloc and calloc automatically add a number of bytes to
  47. either end of the requested malloc. These bytes are filled with a known
  48. pattern so any changes to them can be detected. This detection is performed
  49. when the buffers are freed, or whenever chmall() is called with a NULL ptr.
  50.  
  51. There are three other macros, chmall(), chfree() and testmall(). These
  52. provide front ends to internal functions.
  53.  
  54. There are two ordinary functions which you can call - SetOverlap() and
  55. SetDebugMallocLevel().
  56.  
  57. NOTE: The debug level must be one or more to detect any problems! A value of
  58.       zero effectively disable memory allocation checking.
  59.  
  60. Macros
  61. ------
  62.  
  63. * malloc():   This is a direct replacement for the normal malloc, and no
  64.               special action needs to be taken to use it - just use it
  65.               as malloc in the normal way.
  66.               This will print out a diagnostic if the debug level is three
  67.               or more.
  68.  
  69. * calloc():   Replaces ordinary calloc. See comments above.
  70.               This will print out a diagnostic if the debug level is three
  71.               or more.
  72.  
  73. * free():     Replaces ordinary free. See comments above.
  74.               This will print out a diagnostic if the debug level is three
  75.               or more.
  76.               If the block has had any overlap bytes altered, or it wasn't
  77.               allocated with malloc or calloc, or its been freed twice, a
  78.               diagnostic message if displayed - but only if the debug level
  79.               is one or more.
  80.  
  81. * chmall(x):  Checks the pointer 'x' to see if any illegal overwrites have
  82.               taken place within its "overlap" bytes. If so, a message is
  83.               printed.
  84.               This will print out a diagnostic if the debug level is three
  85.               or more.
  86.  
  87. * chfree():   This will print diagnostics for every unfreed buffer, including
  88.               its size, point of allocation and pointer value.
  89.  
  90. * testmall(x): Displays the amount of memory apparently available for malloc.
  91.                If 'x' is TRUE, individual block sizes comprising the total
  92.                are displayed.
  93.  
  94. Functions:
  95. ----------
  96.  
  97. * SetOverlap(): This should be called once only at the very start of the
  98.                 program to set the size of the overlap buffers. A default
  99.                 of 16 is used if this function is not called.
  100.                 
  101. * SetMallocDebugLevel(): Sets the debug level as follows.
  102.  
  103.     0: No messages printed at all.
  104.     1: Serious errors only printed.
  105.     2: chmall(), chfree() and testmall() data printed (plus the above).
  106.     3: All calls to malloc(), calloc() and free() printed (plus the above).
  107.  
  108.     If you set the debug level to zero, it must be done once only, at the
  109.     very beginning of the program before malloc is called. Thereafter, it
  110.     cannot be called. Otherwise, you can set it to any non-zero value at
  111.     any time to facilitate debugging.
  112.  
  113. How to use it
  114. -------------
  115.  
  116. Using these routines is fairly straightforward:
  117.  
  118. 1) Every module which needs to have memory allocation checking enabled must
  119.    #include "testmall.h" and be recompiled.
  120.  
  121. 2) You must compile "TESTMALL.C" and link with it.
  122.  
  123. 3) You must call the "SetOverlap()" function once only at the beginning
  124.    of main() BEFORE any mallocs take place, unless you are happy with
  125.    the default settings. It is recommended that you use one of the main()
  126.    arguments to set these values, so you can periodically invoke full
  127.    debugging to keep an eye on things, without recompiling.
  128.  
  129. 4) Put judiciously postioned calls to CheckMalloc(), CheckFree() and
  130.    SetDebugMallocLevel() in the code.
  131.  
  132. Limitations
  133. -----------
  134.  
  135. This set of functions was written before heapwalking functions were commonly
  136. available in libraries - it therefore makes no use of them.
  137.  
  138. You can call SetDebugMallocLevel() at any time with a value of one or more.
  139. You may call it only once at the beginning of a program with a value of
  140. zero, and thereafter it may not be called with a non-zero value.
  141.  
  142. This malloc replacement is not really suitable for C++, since it doesn't handle
  143. "new" and "delete". I have another version for C++, which could be made
  144. available.
  145.  
  146. Only malloc(), calloc() and free() are supported. This means that the use of
  147. functions such as realloc() will confuse the system, and cause it to print
  148. erroneous reports.
  149.  
  150. Use of these functions slows down the memory allocation substantially,
  151. particularly if debug output is enabled.
  152.  
  153. Since extra memory is allocated for each block, you can run out of memory
  154. extremely quickly - especially if a large number of nominally small blocks
  155. are being allocated. The solution, such as it is, is to reduce the overlap
  156. size somewhat. Ideally, it should be possible to turn on and off malloc
  157. checking for particular pointers, or perhaps to set a "minimum check" size,
  158. below which blocks are not checked.
  159.  
  160. The functions assume that the __FILE__ macro will only generate filenames
  161. without a path - they must be less than
  162.  
  163. Not *all* malloc bugs can be caught using these functions; however, by
  164. changing the overlap size, and calling CheckMalloc() frequently (with a
  165. NULL pointer parameter - see function listing below) you can usually locate
  166. the majority of bugs.
  167.  
  168. It is possible for a pointer to allocated in a module without malloc debugging
  169. support, yet freed in a module which has got debugging support. This will cause
  170. the free() to erroneously report that an unmalloced pointer is being freed.
  171. For this reason, it is recommended that *all* modules which you have access to
  172. are recompiled, if it is feasible.
  173.  
  174. Similar problems will occur if you willy-nilly call SetOverlap() at any time.
  175.  
  176. All diagnostic output is sent to stdout using printf(). This may interfere
  177. with program output. It would be relatively easy to change all the printfs
  178. to fprintfs and redirect output to either a file or a printer.
  179.  
  180. *******************************************************************************/
  181.  
  182. #if    defined (DBG_MALLOC)
  183. #include <stdio.h>
  184. #include <malloc.h>
  185. #include <string.h>
  186. #include <memory.h>
  187. #include <stdlib.h>
  188. #include <stdarg.h>
  189. #include <alloc.h>
  190.  
  191. #define PATHLEN         16      // Max length of __FILE__ filenames.
  192. #define NAMECOUNT       64      // Max. No. of modules calling malloc().
  193. #define MALLCOUNT     4096      // Maximum number of concurrent mallocs.
  194.  
  195. static int overlap = 16; // The #extra bytes to either side of malloced blocks.
  196.                          // Set with "SetOverlap()", or defaults to 16.
  197.  
  198. //=============================================================================
  199. // The static global DebugMalloc is used to determine the level of debugging:
  200. //----------------------------------------------------------------------------
  201. // 0: No messages printed at all.
  202. // 1: Serious errors only printed.
  203. // 2: chmall(), chfree() and testmall() data printed (plus the above).
  204. // 3: All calls to malloc(), calloc() and free() printed (plus the above).
  205. //
  206. // The value can be changed at any time before the first malloc() takes place
  207. // by calling "SetDebugMallocLevel()".
  208. //=============================================================================
  209.  
  210. static int DebugMalloc = 1;
  211.  
  212. //=============================================================================
  213. // To use the malloc debug functions, include the following #defines int the
  214. // sources that you want to check: (These are in "TESTMALL.H")
  215. //-----------------------------------------------------------------------------
  216. // #define malloc(x)        mymalloc(x,__FILE__,__LINE__)
  217. // #define calloc(x,y)      mycalloc(x,y,__FILE__,__LINE__)
  218. // #define free(x)          myfree(x,__FILE__,__LINE__)
  219. // #define chmall(x)        CheckMalloc(x,__FILE__,__LINE__)
  220. // #define chfree()         CheckFree(__FILE__,__LINE__)
  221. // #define testmall(f)      TestMall(f,__FILE__,__LINE__)
  222. //=============================================================================
  223.  
  224. #ifdef malloc
  225.     #undef malloc
  226. #endif
  227.  
  228. #ifdef calloc
  229.     #undef calloc
  230. #endif
  231.  
  232. #ifdef free
  233.     #undef free
  234. #endif
  235.  
  236.  
  237. static struct mallstr
  238. {
  239.     int         name;
  240.     int         line;
  241.     unsigned    size;
  242.     void        *ptr;
  243. }
  244. malldata[MALLCOUNT];
  245.  
  246. static char mallfiles[NAMECOUNT][PATHLEN];
  247.  
  248. static int mallcount, namecount;
  249.  
  250. void CheckMalloc( void *p, char *filename, int linenum );
  251. void myfree( unsigned char *p, char *filename, int linenum );
  252. void *mycalloc( size_t size, int count, char *filename, int linenum );
  253. void *mymalloc( size_t size, char *filename, int linenum );
  254. void TestMalloc( int blocks, char *filename, int linenum );
  255. void CheckFree( char *file, int line );
  256. void mem_report (void);
  257.  
  258. static void addmall( char *p, unsigned size, char *name, int line );
  259. static void printmalldata( void *p );
  260. static void CheckBuffer( unsigned char *p, char *filename, int linenum );
  261. static void out_log (char *fmt, ...);
  262.  
  263. /*******************************************************************************
  264. * void SetOverlap( int over )                                                  *
  265. *------------------------------------------------------------------------------*
  266. * Sets the amount over overlap at either ends of malloced buffers.             *
  267. * IMPORTANT: Must be called once only, at the beginning of the program BEFORE  *
  268. * ~~~~~~~~~  an memory allocations have been done.                             *
  269. *******************************************************************************/
  270.  
  271. void SetOverlap( int over )
  272. {
  273.     overlap = over;
  274. }
  275.  
  276.  
  277. /*******************************************************************************
  278. * void SetDebugMallocLevel( int level )                                        *
  279. *------------------------------------------------------------------------------*
  280. * Sets the malloc debugging level. Value may be:                               *
  281. *                                                                              *
  282. * 0: No messages printed at all.                                               *
  283. * 1: Serious errors only printed.                                              *
  284. * 2: chmall(), chfree() and testmall() data printed (plus the above).          *
  285. * 3: All calls to malloc(), calloc() and free() printed (plus the above).      *
  286. *******************************************************************************/
  287.  
  288. void SetDebugMallocLevel( int level )
  289. {
  290.     DebugMalloc = level;
  291. }
  292.  
  293.  
  294. /*******************************************************************************
  295. * void TestMalloc( int blocks, char *filename, int line )                      *
  296. *------------------------------------------------------------------------------*
  297. * Displays the amount of memory available for mallocing.                       *
  298. * If blocks is TRUE, the individual blocks comprising this are displayed.      *
  299. * This should be called via the testmall() macro.                              *
  300. *******************************************************************************/
  301.  
  302. void TestMalloc( int blocks, char *filename, int line )
  303. {
  304.     static void *p[128];
  305.  
  306.     unsigned size;
  307.     long     total;
  308.     int      count;
  309.     int      num;
  310.  
  311.     if (!DebugMalloc)
  312.         return;
  313.  
  314.     size = 65535U;
  315.     count = 0;
  316.     total = 0L;
  317.  
  318.     while ( size > 16 )
  319.     {
  320.         num = 0;
  321.  
  322.         while ( (p[count]=malloc(size)) != NULL )
  323.         {
  324.             num++;
  325.             count++;
  326.             total += size;
  327.  
  328.             if ( count >= (sizeof(p)/sizeof(p[0])) )
  329.             {
  330.                 out_log("Too many blocks in function TestMalloc()!\n");
  331.                 exit(3);
  332.             }
  333.         }
  334.  
  335.         if (num&&blocks)
  336.             out_log("%d X %u\n", num, size );
  337.  
  338.         if ( size >= 512 )
  339.             size -= 256;
  340.         else
  341.             size -= 16;
  342.     }
  343.  
  344.     out_log("%ld bytes free at (%s:%d)\n", total, filename, line );
  345.  
  346.     while (count)
  347.         free(p[--count]);
  348. }
  349.  
  350.  
  351. /*******************************************************************************
  352. * void *mymalloc( size_t size, char *filename, int linenum )                   *
  353. *------------------------------------------------------------------------------*
  354. * Replacement for malloc(), called with the malloc() macro.                    *
  355. *******************************************************************************/
  356.  
  357. void *mymalloc( size_t size, char *filename, int linenum )
  358. {
  359.     char *p;
  360.  
  361.     if (!DebugMalloc)
  362.         return malloc(size);
  363.  
  364.     p = (char *) malloc(size+overlap*2+sizeof(unsigned));
  365.  
  366.     if ( p == NULL )
  367.         return NULL;
  368.  
  369.     addmall(p,size,filename,linenum);
  370.     *(unsigned*)p = size;
  371.  
  372.     if ( DebugMalloc >= 3 )
  373.         out_log("Malloc  %5u  %p  %12.12s:%5d\n", size, p+(overlap+sizeof(unsigned)), filename, linenum );
  374.  
  375.     memset(p+2,0xAA,overlap);
  376.     memset(p+(overlap+sizeof(unsigned))+size,0xAA,overlap);
  377.     return p+(overlap+sizeof(unsigned));
  378. }
  379.  
  380. /*******************************************************************************
  381. * void *mycalloc( size_t size, int count, char *filename, int linenum )        *
  382. *------------------------------------------------------------------------------*
  383. * Replacement for calloc() called by the calloc() macro.                       *
  384. *******************************************************************************/
  385.  
  386. void *mycalloc( size_t size, int count, char *filename, int linenum )
  387. {
  388.     char *p;
  389.  
  390.     if (!DebugMalloc)
  391.         return calloc(size,count);
  392.  
  393.     size *= (unsigned)count;
  394.     p = (char *) malloc(size+overlap*2+sizeof(unsigned));
  395.  
  396.     if ( p == NULL )
  397.         return NULL;
  398.  
  399.     addmall(p,size,filename,linenum);
  400.     *(unsigned*)p = size;
  401.  
  402.     if ( DebugMalloc >= 3 )
  403.         out_log("Calloc  %5u  %8p  %12.12s:%5d\n", size, p+(overlap+sizeof(unsigned)), filename, linenum );
  404.  
  405.     memset(p+2,0xAA,overlap);
  406.     memset(p+(overlap+sizeof(unsigned))+size,0xAA,overlap);
  407.     memset(p+(overlap+sizeof(unsigned)),0,size);
  408.     return p+(overlap+sizeof(unsigned));
  409. }
  410.  
  411.  
  412. /*******************************************************************************
  413. * void myfree( unsigned char *p, char *filename, int linenum )                 *
  414. *------------------------------------------------------------------------------*
  415. * Replacement for free() called by the free() macro.                           *
  416. *******************************************************************************/
  417.  
  418. void myfree( unsigned char *p, char *filename, int linenum )
  419. {
  420.     int i, found;
  421.  
  422.     if ( p == NULL )        // If it's NULL, then we don't need to worry!
  423.         return;
  424.  
  425.     if (!DebugMalloc)
  426.     {
  427.         free(p);
  428.         return;
  429.     }
  430.  
  431.     // First check if this pointer is actually stored in the pointer list...
  432.  
  433.     for ( i = found = 0  ;  !found && i<mallcount  ;  i++ )
  434.     {
  435.         if ( malldata[i].ptr == p )
  436.         {
  437.             found = 1;
  438.             break;
  439.         }
  440.     }
  441.  
  442.     if (!found)         // Whoops!
  443.     {
  444.         out_log("-------------------------------------------------------------------------------\n");
  445.         out_log("ERROR: Attempting to free a pointer not in malloc list!\n");
  446.         out_log("Could have been freed twice or not malloced or malloced in a different module.\n");
  447.         out_log("Pointer:%p, at (%s:%d)\n", p, filename, linenum );
  448.         out_log("-------------------------------------------------------------------------------\n");
  449.         return;
  450.     }
  451.  
  452.     CheckMalloc(p,filename,linenum);
  453.  
  454.     if ( DebugMalloc >= 3 )
  455.     {
  456.         out_log
  457.         (
  458.             "Free:   %5u  %8p  %12.12s:%5d <-- %12.12s:%5d\n",
  459.             malldata[i].size,
  460.             p, filename, linenum,
  461.             mallfiles[malldata[i].name],
  462.             malldata[i].line
  463.         );
  464.     }
  465.  
  466.     // OK - we've found it, so remove it from the list...
  467.  
  468.     for ( --mallcount  ;  i < mallcount  ;  i++ )
  469.         malldata[i] = malldata[i+1];
  470.  
  471.     free(p-(overlap+sizeof(unsigned)));
  472. }
  473.  
  474.  
  475. /*******************************************************************************
  476. * void CheckMalloc( void *p, char *filename, int linenum )                     *
  477. *------------------------------------------------------------------------------*
  478. * Checks the given pointer (which must have been malloced with mymalloc())     *
  479. * for buffer limit overwrite violations.                                       *
  480. * If 'p' is NULL, all currently malloced blocks are checked.                   *
  481. *******************************************************************************/
  482.  
  483. void CheckMalloc( void *p, char *filename, int linenum )
  484. {
  485.     int         i;
  486.  
  487.     if (!DebugMalloc)
  488.         return;
  489.  
  490.     if (p)      // Only want to check this pointer...
  491.     {
  492.         CheckBuffer((unsigned char *) p,filename,linenum);
  493.         return;
  494.     }
  495.  
  496.     for ( i = 0  ;  i < mallcount  ;  i++ )
  497.         CheckBuffer((unsigned char *) malldata[i].ptr, filename, linenum );
  498. }
  499.  
  500.  
  501. /*******************************************************************************
  502. * void CheckFree( char *file, int line )                                       *
  503. *------------------------------------------------------------------------------*
  504. * Displays a list of any mallocs which have not yet been freed.                *
  505. *******************************************************************************/
  506.  
  507. void CheckFree( char *file, int line )
  508. {
  509.     int     i;
  510.  
  511.     if ( DebugMalloc < 2 )
  512.         return;
  513.  
  514.     if (mallcount==0)
  515.     {
  516.         out_log("No unfreed mallocs at (%s:%d)\n", file, line );
  517.         return;
  518.     }
  519.  
  520.     out_log("==============================================\n");
  521.     out_log("List of unfreed mallocs at (%s:%d)\n", file, line );
  522.     out_log("==============================================\n");
  523.     out_log("|  Pointer  |  Size |  File Name   |  Line |\n");
  524.     out_log("+-----------+-------+--------------+-------+\n");
  525.  
  526.     for ( i = 0  ;  i < mallcount  ;  i++ )
  527.         out_log
  528.         (
  529.             "| %9p | %5u | %-12.12s | %5d |\n",
  530.             malldata[i].ptr,
  531.             malldata[i].size,
  532.             mallfiles[malldata[i].name],
  533.             malldata[i].line
  534.         );
  535.  
  536.     out_log("+-----------+-------+--------------+-------+\n");
  537. }
  538.  
  539.  
  540. static void CheckBuffer( unsigned char *p, char *filename, int linenum )
  541. {
  542.     unsigned    len;
  543.     int         i;
  544.  
  545.     len = *(unsigned*)(p-(overlap+sizeof(unsigned)));
  546.  
  547.     for ( i = 0  ;  i < overlap  ;  i++ )
  548.     {
  549.         if ( *(p-overlap+i) != 0xAA )
  550.         {
  551.             out_log("-------------------------------------------------------------------------------\n");
  552.             out_log("ERROR at (%s:%d), pointer = %8p\n", filename, linenum, p );
  553.             out_log("Illegal buffer access detected at beginning of buffer!\n");
  554.             printmalldata(p);
  555.             out_log("-------------------------------------------------------------------------------\n");
  556.             break;
  557.         }
  558.     }
  559.  
  560.     for ( i = 0  ;  i < overlap  ;  i++ )
  561.     {
  562.         if ( *(p+i+len) != 0xAA )
  563.         {
  564.             out_log("-------------------------------------------------------------------------------\n");
  565.             out_log("ERROR at (%s:%d), pointer = %8p\n", filename, linenum, p );
  566.             out_log("Illegal buffer access detected at end of buffer!\n");
  567.             printmalldata(p);
  568.             out_log("-------------------------------------------------------------------------------\n");
  569.             break;
  570.         }
  571.     }
  572. }
  573.  
  574.  
  575. static void printmalldata( void *p )
  576. {
  577.     int     i;
  578.  
  579.     for ( i = 0  ;  i < mallcount  ;  i++ )
  580.     {
  581.         if ( malldata[i].ptr == p )
  582.         {
  583.             out_log
  584.             (
  585.                 "Pointer:%8p, size:%u, malloced at %s:%d\n",
  586.                 malldata[i].ptr,
  587.                 malldata[i].size,
  588.                 mallfiles[malldata[i].name],
  589.                 malldata[i].line
  590.             );
  591.  
  592.             return;
  593.         }
  594.     }
  595.  
  596.     out_log("Cannot find any malloc data for pointer %8p\n", p );
  597. }
  598.  
  599.  
  600. static void addmall( char *p, unsigned size, char *name, int line )
  601. {
  602.     int     i;
  603.  
  604.     p += (overlap+sizeof(unsigned));
  605.  
  606.     if ( mallcount >= MALLCOUNT )
  607.     {
  608.         out_log("-------------------------------------------------------------------------------\n");
  609.         out_log("ERROR IN ADDMALL! Not enough space to save data!\n");
  610.         out_log("Pointer:%8p, size:%u name:%s, line:%d\n", p, size, name, line );
  611.         out_log("-------------------------------------------------------------------------------\n");
  612.         return;
  613.     }
  614.  
  615.     malldata[mallcount].line = line;
  616.     malldata[mallcount].size = size;
  617.     malldata[mallcount].ptr  = p;
  618.  
  619.     // See if name is already defined, and we don't need to re-store it...
  620.  
  621.     for ( i = 0  ;  i < namecount  ;  i++ )
  622.     {
  623.         if ( strcmp( mallfiles[i], name ) == 0 )
  624.         {
  625.             malldata[mallcount++].name = i;
  626.             return;
  627.         }
  628.     }
  629.  
  630.     // Couldn't find name - need to add it to list...
  631.  
  632.     if ( namecount >= NAMECOUNT )
  633.     {
  634.         out_log("-------------------------------------------------------------------------------\n");
  635.         out_log("ERROR IN ADDMALL! Not enough space to save file name!\n");
  636.         out_log("Pointer:%8p, size:%u name:%s, line:%d\n", p, size, name, line );
  637.         out_log("-------------------------------------------------------------------------------\n");
  638.         return;
  639.     }
  640.  
  641.     if (strlen(name)>=PATHLEN)
  642.     {
  643.         out_log("Pathname too long - increase size of PATHLEN in TESTMALL.C");
  644.         name = "!!LONG NAME!!";
  645.     }
  646.  
  647.     malldata[mallcount++].name = namecount;
  648.     strcpy( mallfiles[namecount++], name );
  649. }
  650.  
  651.  
  652. /****************************************************************************
  653. *    mem_report                                                                *
  654. *    Miscellaneous memory statistics.                                        *
  655. ****************************************************************************/
  656.  
  657. void mem_report (void)
  658.     {
  659.     out_log ("farcoreleft = %lu bytes\n", farcoreleft ());
  660.     }    /* void mem_report (void) */
  661.     
  662.  
  663. /****************************************************************************
  664. *    out_log                                                                    *
  665. *    Dump debug output to out_file.                                            *
  666. ****************************************************************************/
  667.  
  668. static void out_log (char *fmt, ...)
  669.     {
  670.     va_list argptr;                        /* -> variable arguments            */
  671.     FILE *out_fp;                        /* -> output file                    */
  672.  
  673.     if ((out_fp = fopen ("testmall.out", "a")) != NULL)
  674.         {
  675.         va_start (argptr, fmt);
  676.         (void) vfprintf (out_fp, fmt, argptr);
  677.         va_end (argptr);
  678.         (void) fclose (out_fp);
  679.         }
  680.  
  681.     }    /* static void out_log (char *fmt, ...) */
  682.  
  683. #endif
  684.